package top.fangw.oauth.handler;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import lombok.RequiredArgsConstructor;
import org.springframework.security.oauth2.core.AuthorizationGrantType;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClient;
import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository;
import org.springframework.security.oauth2.server.authorization.settings.ClientSettings;
import org.springframework.security.oauth2.server.authorization.settings.ConfigurationSettingNames;
import org.springframework.security.oauth2.server.authorization.settings.OAuth2TokenFormat;
import org.springframework.security.oauth2.server.authorization.settings.TokenSettings;
import top.fangw.common.api.dto.Oauth2RegisteredClientDto;
import top.fangw.common.api.openfign.tenant.IRegisteredClientServiceFeign;
import top.fangw.common.core.result.Result;

import java.math.BigDecimal;
import java.time.Duration;
import java.time.ZoneOffset;
import java.util.Arrays;

/**
 * @description:
 * @author: fgw
 * @createDate: 2024/2/22
 */
@RequiredArgsConstructor
public class MyRegisteredClientRepository implements RegisteredClientRepository {

    private final IRegisteredClientServiceFeign registeredClientServiceFeign;

    @Override
    public void save(RegisteredClient registeredClient) {

    }

    @Override
    public RegisteredClient findById(String id) {
        Oauth2RegisteredClientDto registeredClientDto =  null;
        Result<Oauth2RegisteredClientDto> byIdFeign = registeredClientServiceFeign.getByIdFeign(id);
        if(byIdFeign.getStatus()){
            registeredClientDto = byIdFeign.getData();
        }
        return buildRegisteredClient(registeredClientDto);
    }

    @Override
    public RegisteredClient findByClientId(String clientId) {
        Result<Oauth2RegisteredClientDto> oauth2RegisteredClientDtoResult = registeredClientServiceFeign.geyByClientIdFeign(clientId);
        Oauth2RegisteredClientDto registeredClientDto = null;
        if(oauth2RegisteredClientDtoResult.getStatus()){
            registeredClientDto = oauth2RegisteredClientDtoResult.getData();
        }
        return buildRegisteredClient(registeredClientDto);
    }

    private Duration getDuration(JSONArray jsonArray) {
        BigDecimal o = (BigDecimal) jsonArray.get(1);
        return Duration.ofSeconds(o.longValue());
    }

    private RegisteredClient buildRegisteredClient(Oauth2RegisteredClientDto registeredClientDto){
        JSONObject jsonObject = JSONObject.parseObject(registeredClientDto.getClientSettings());
        Boolean requireProofKey = jsonObject.getBoolean(ConfigurationSettingNames.Client.REQUIRE_PROOF_KEY);
        Boolean requireAuthorizationConsent = jsonObject.getBoolean(ConfigurationSettingNames.Client.REQUIRE_AUTHORIZATION_CONSENT);
        ClientSettings clientSettings = ClientSettings.builder()
                .requireProofKey(requireProofKey)
                .requireAuthorizationConsent(requireAuthorizationConsent)
                .build();
        JSONObject jsonObject1 = JSONObject.parseObject(registeredClientDto.getTokenSettings());
        TokenSettings tokenSettings = TokenSettings.builder()
                .authorizationCodeTimeToLive(getDuration(jsonObject1.getJSONArray(ConfigurationSettingNames.Token.AUTHORIZATION_CODE_TIME_TO_LIVE)))
                .accessTokenTimeToLive(getDuration(jsonObject1.getJSONArray(ConfigurationSettingNames.Token.ACCESS_TOKEN_TIME_TO_LIVE)))
                .refreshTokenTimeToLive(getDuration(jsonObject1.getJSONArray(ConfigurationSettingNames.Token.REFRESH_TOKEN_TIME_TO_LIVE)))
                .deviceCodeTimeToLive(getDuration(jsonObject1.getJSONArray(ConfigurationSettingNames.Token.DEVICE_CODE_TIME_TO_LIVE)))
                .accessTokenFormat(new OAuth2TokenFormat(jsonObject1.getJSONObject(ConfigurationSettingNames.Token.ACCESS_TOKEN_FORMAT).getString("value")))
                .idTokenSignatureAlgorithm(SignatureAlgorithm.from(jsonObject1.getJSONArray(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM).get(1).toString()))
                .reuseRefreshTokens(jsonObject1.getBoolean(ConfigurationSettingNames.Token.REUSE_REFRESH_TOKENS))
                .build();
        RegisteredClient.Builder builder = RegisteredClient.withId(registeredClientDto.getId())
                .clientId(registeredClientDto.getClientId())
                .clientIdIssuedAt(registeredClientDto.getClientIdIssuedAt().toInstant(ZoneOffset.UTC))
                .clientSecret(registeredClientDto.getClientSecret())
                .clientSecretExpiresAt(registeredClientDto.getClientSecretExpiresAt() == null ? null : registeredClientDto.getClientSecretExpiresAt().toInstant(ZoneOffset.UTC))
                .clientName(registeredClientDto.getClientName())
                .clientSettings(clientSettings)
                .tokenSettings(tokenSettings);

        Arrays.stream(registeredClientDto.getClientAuthenticationMethods().split(",")).forEach(item -> builder.clientAuthenticationMethod(new ClientAuthenticationMethod(item)));
        Arrays.stream(registeredClientDto.getAuthorizationGrantTypes().split(",")).forEach(item -> builder.authorizationGrantType(new AuthorizationGrantType(item)));
        Arrays.stream(registeredClientDto.getRedirectUris().split(",")).forEach(builder::redirectUri);
        Arrays.stream(registeredClientDto.getPostLogoutRedirectUris().split(",")).forEach(builder::postLogoutRedirectUri);
        Arrays.stream(registeredClientDto.getScopes().split(",")).forEach(builder::scope);
        return builder.build();
    }
}
